// Copyright 2011, Vanya Davidenko.
// Используемая кодировка: UTF-8.

#ifndef LIBV_FILE_WINDOWS_ACFILE_IMPL_HPP_
#define LIBV_FILE_WINDOWS_ACFILE_IMPL_HPP_

#ifndef _WIN32
# error "This header should be used in Windows only."
#endif

#include <assert.h>
#include <string>
#include <libv/stdint.h>
#include <libv/windows_lean.h>
#include <libv/file/basic_file.h>



namespace libv
{

bool ACFile::OpenImpl(const ::std::string& file_path,
                      const int open_type,
                      const bool enable_read_ahead_caching,
                      const bool read_only) const
{
    return BasicFile::BasicOpen(file_path,
                                open_type,
                                enable_read_ahead_caching,
                                FILE_FLAG_OVERLAPPED,
                                read_only);
}



bool ACFile::WriteImpl(const fpos64& offset,
                       const void* data,
                       const uint32_t data_size,
                       ACFile::RequestCompletionHandle* completion_handle)
{
    OVERLAPPED o = { 0 };
    if ( !InitOverlapped(offset, &o) )
        return false;  // last_error_ заполнил InitOverlapped.
    RequestCompletionHandle c(file_handle_, o, data_size,
                              RequestCompletionHandle::WRITING);

    // Бесконечный цикл запросов на асинхронную операцию.
    // Будет исполняться несколько раз в случае,
    // если ОС возвращает ошибку "слишком много одновременных запросов".
    for ( ; ; )
    {
        // Возвращает: FALSE, если запрос выполняется асинхронно
        //             TRUE, если запрос выполнен синхронно (при выполнении
        //                   дополнительных служебных операций).
        const bool result = ( ::WriteFile(
            file_handle_,
            data,
            data_size,
            NULL,
            c.request_handle_) != FALSE );
        last_error_ = ::GetLastError();

        if ( result )  // запрос уже был выполнен синхронно
        {
            *completion_handle = c;
            return true;
        }


        switch ( last_error_ )
        {
            // Успешная отправка запроса.
            case ERROR_IO_PENDING:
            {
                *completion_handle = c;
                return true;
            }


            // Слишком много одновременных асинхронных запросов,
            // нужно повторить операцию позднее.
            case ERROR_INVALID_USER_BUFFER:
            case ERROR_NOT_ENOUGH_MEMORY:
            {
                ::Sleep(1);  // повтор в следующий квант
                break;  // повтор запроса WriteFile
            }


            // Для некоторых устройств необходимо гарантировать,
            // что буфер не скинется в файл подкачки, поэтому WriteFile
            // попробует заблокировать страницы буфера, чтобы они оставались
            // в памяти. Но число таких блокировок ограничено
            // и в случае нехватки возвращается следующая ошибка.
            case ERROR_NOT_ENOUGH_QUOTA:
            default:
                return false;
        }
    }
}



bool ACFile::ReadImpl(const fpos64& offset,
                      volatile void* buffer,
                      const uint32_t buffer_size,
                      ACFile::RequestCompletionHandle* completion_handle) const
{
    OVERLAPPED o = { 0 };
    if ( !InitOverlapped(offset, &o) )
        return false;  // last_error_ заполнил InitOverlapped.
    RequestCompletionHandle c(file_handle_, o, buffer_size,
                              RequestCompletionHandle::READING);

    // Линукс правильно требует volatile на указатель буфера.
    // Но Майкрософт так не считает, так что делаем грубое приведение типов.
    LPVOID nv_buffer = const_cast<void*>(buffer);

    // Бесконечный цикл запросов на асинхронную операцию.
    // Будет исполняться несколько раз в случае,
    // если ОС возвращает ошибку "слишком много одновременных запросов".
    for ( ; ; )
    {
        // Возвращает: FALSE, если запрос выполняется асинхронно
        //             TRUE, если запрос выполнен синхронно (при выполнении
        //                   дополнительных служебных операций).
        const bool result = ::ReadFile(
            file_handle_,  // хэндл файла
            nv_buffer,     // буфер (не должен изменяться до COMPLETED)
            buffer_size,   // размер буфера
            NULL,          // для асинхронных операций игнорируется
            c.request_handle_) != FALSE;
        last_error_ = ::GetLastError();

        if ( result )  // запрос уже был выполнен синхронно
        {
            *completion_handle = c;
            return true;
        }


        switch ( last_error_ )
        {
            // Успешная отправка запроса.
            case ERROR_IO_PENDING:
            {
                *completion_handle = c;
                return true;
            }


            // Слишком много одновременных асинхронных запросов,
            // нужно повторить операцию позднее.
            case ERROR_INVALID_USER_BUFFER:
            case ERROR_NOT_ENOUGH_MEMORY:
            {
                ::Sleep(1);
                break;  // повтор запроса ReadFile
            }


            // Для некоторых устройств необходимо гарантировать,
            // что буфер не скинется в файл подкачки, поэтому ReadFile
            // попробует заблокировать страницы буфера, чтобы они оставались
            // в памяти. Но число таких блокировок ограничено
            // и в случае нехватки возвращается следующая ошибка.
            case ERROR_NOT_ENOUGH_QUOTA:
            default:
                return false;
        }
    }
}



bool ACFile::InitOverlapped(const fpos64 offset, OVERLAPPED* overlapped) const
{
    OVERLAPPED new_overlapped = { 0 };
    ULARGE_INTEGER li_offset;
    li_offset.QuadPart = static_cast<ULONGLONG>(offset);
    new_overlapped.Offset = li_offset.LowPart;
    new_overlapped.OffsetHigh = li_offset.HighPart;

    // Создаем событие, которое мигнет, когда операция завершится.
    new_overlapped.hEvent = ::CreateEvent(
        NULL,   // атрибуты безопасности
        TRUE,   // ручное переключение, требование MSDN
        FALSE,  // начальное состояние — выключено
        NULL);  // имя события
    last_error_ = ::GetLastError();

    if ( new_overlapped.hEvent == NULL )
        return false;

    *overlapped = new_overlapped;
    return true;
}

}  // libv

#endif  // LIBV_FILE_WINDOWS_ACFILE_IMPL_HPP_
